package com.aponisipsis.gl3d.geometry;

import java.nio.FloatBuffer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import org.lwjgl.opengl.ARBMultitexture;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;
import org.lwjgl.opengl.GL13;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL30;
import org.lwjgl.util.Color;
import org.newdawn.slick.opengl.Texture;

import com.aponisipsis.gl3d.data.ResourceManager;

import util.Memory;

public class Material{	
	FloatBuffer diffuse;
	FloatBuffer ambient;
	FloatBuffer specular;
	FloatBuffer emissive;
	FloatBuffer shininess;
	
	boolean smooth;
	
	//Shader shader;
	
	List<String> textureNames;
	List<String> uvMapNames;
	
	List<TextureUnit> textureUnits;
	
	private boolean hasNormalMap;
	
	public Material(){
		diffuse = Memory.floatBuffer(4);
		ambient = Memory.floatBuffer(4);
		specular = Memory.floatBuffer(4);
		emissive = Memory.floatBuffer(4);
		shininess = Memory.floatBuffer(4);
		textureNames = new ArrayList<String>();
		uvMapNames = new ArrayList<String>();
		
		textureUnits = new ArrayList<TextureUnit>();
		
		smooth = true;
		//shader = null;
		hasNormalMap = false;
	}
	
	public Material(float[] diffuse, float[] ambient, float[] specular, float[] emissive, float[] shininess, boolean smooth){
		this();
		setDiffuse(diffuse[0], diffuse[1], diffuse[2], diffuse[3]);
		setAmbient(ambient[0], ambient[1], ambient[2], ambient[3]);
		setSpecular(specular[0], specular[1], specular[2], specular[3]);
		setEmissive(emissive[0], emissive[1], emissive[2], emissive[3]);
		setShininess(shininess[0], shininess[1], shininess[2], shininess[3]);
		
		this.smooth=smooth;
	}
	
//	public void setShader(Shader s){
//		this.shader=s;
//	}
//	
//	public Shader getShader(){
//		return this.shader;
//	}
	
	public void setDiffuse(float r, float g, float b, float a){
		diffuse.put(new float[]{r,g,b,a}).flip();
	}
	
	public void setAmbient(float r, float g, float b, float a){
		ambient.put(new float[]{r,g,b,a}).flip();
	}
	
	public void setSpecular(float r, float g, float b, float a){
		specular.put(new float[]{r,g,b,a}).flip();
	}
	
	public void setEmissive(float r, float g, float b, float a){
		emissive.put(new float[]{r,g,b,a}).flip();
	}
	
	public void setShininess(float r, float g, float b, float a){
		shininess.put(new float[]{r,g,b,a}).flip();
	}
	
	public static Material createDefault(float r, float g, float b, float a, float shiny){
		Material material = new Material();

        float[] reflect = {shiny,shiny,shiny,1}; // make a shade of gray
        float[] ambient = {r*.5f,g*.5f,b*.5f,1};  // darker surface color
        
        material.diffuse.put(new float[]{r,g,b,a}).flip();     // surface directly lit
        material.ambient.put(ambient).flip();          // surface in shadow
        material.specular.put(reflect).flip();         // reflected light
        material.emissive.put(new float[]{0,0,0,1}).flip();      // no emissive light
        
        // size of reflection
        int openglShininess = ((int)(shiny*127f));   // convert 0-1 to 0-127
        if (openglShininess >= 0 && openglShininess <= 127) {
        	material.shininess.put(new float[] {openglShininess,0,0,0}).flip();
        }
        
		return material;
	}

	public boolean isSmooth(){
		return smooth;
	}
	
	public void setSmooth(boolean smooth){
		this.smooth=smooth;
	}
	
	public void render() {
		GL11.glDisable(GL11.GL_COLOR_MATERIAL);
		GL11.glColorMaterial(GL11.GL_FRONT, GL11.GL_AMBIENT_AND_DIFFUSE);
		GL11.glMaterial(GL11.GL_FRONT_AND_BACK, GL11.GL_AMBIENT_AND_DIFFUSE, diffuse);
        GL11.glMaterial(GL11.GL_FRONT_AND_BACK, GL11.GL_DIFFUSE, diffuse);
        GL11.glMaterial(GL11.GL_FRONT_AND_BACK, GL11.GL_AMBIENT, ambient);
        GL11.glMaterial(GL11.GL_FRONT_AND_BACK, GL11.GL_SPECULAR, specular);
        GL11.glMaterial(GL11.GL_FRONT_AND_BACK, GL11.GL_EMISSION, emissive);
        GL11.glMaterial(GL11.GL_FRONT_AND_BACK, GL11.GL_SHININESS, shininess);

        if(smooth){
        	GL11.glShadeModel(GL11.GL_SMOOTH);
        }else{
        	GL11.glShadeModel(GL11.GL_FLAT);
        }
        
        GL11.glEnable(GL11.GL_BLEND);
        GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
        
        boolean previousNormalMap = false;
        
        if(textureUnits.size()>0){
        	int curTxId = GL13.GL_TEXTURE0;
        	
        	for(TextureUnit unit:textureUnits){
// Bump Mapping code
//            	ARBMultitexture.glActiveTextureARB(curTxId);
//            	GL11.glEnable(GL11.GL_TEXTURE_2D);
//            	unit.getTexture().bind();
//            	
//            	if(unit.isNormalMap()){
//            		GL11.glTexEnvi(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL13.GL_COMBINE);
//            		GL11.glTexEnvi(GL11.GL_TEXTURE_ENV, GL13.GL_COMBINE_RGB, GL13.GL_DOT3_RGB) ;
//            		GL11.glTexEnvi(GL11.GL_TEXTURE_ENV, GL13.GL_SOURCE0_RGB, GL13.GL_PREVIOUS) ;
//            	}else{
//            		GL11.glTexEnvi(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL11.GL_MODULATE);
//            	}
            	GL11.glEnable(GL11.GL_TEXTURE_2D);
            	GL13.glActiveTexture(curTxId);
            	unit.getTexture().bind();
            	/**
            	 * glTexEnvi, con pname=GL_TEXTURE_ENV_MODE peude tomar los valores:
            	 * 	GL_MODULATE=
            	 *  GL_DECAL= parece apilar las texturas
            	 *  GL_BLEND,
            	 *  GL_REPLACE= reemplaza la textura anterior
            	 */
            	GL11.glTexEnvi(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL11.GL_REPLACE);
            	curTxId++;
            }
            
        }
//        for(TextureUnit unit:textureUnits){
//        	ARBMultitexture.glActiveTextureARB(GL11.gl_texture)
//        	GL11.glEnable(GL11.GL_TEXTURE_2D);
//        }
//        if(textureNames.size()>0){
//        	GL11.glEnable(GL11.GL_TEXTURE_2D);
//        	int curTx = GL13.GL_TEXTURE0;
//        	for(String texture:textureNames){
//        		Texture t = ResourceManager.getTexture(texture);
//        		GL13.glActiveTexture(curTx);
//        		
//        		t.bind();
//        	}
//        }
       // if(shader!=null)GL20.glLinkProgram(shader.getShader());

//        if(texture!=null){
//        	GL13.glActiveTexture(GL13.GL_TEXTURE0);
//        	texture.bind();
//        }
//        
//       if(bump!=null){
//        	GL13.glActiveTexture(GL13.GL_TEXTURE1);
//        	bump.bind();
//        }
//       GL13.glActiveTexture(GL13.GL_TEXTURE1);
//       if(shader!=null){
//    	   GL20.glUseProgram(shader.getShader());
//	       
//	       GL20.glUniform1i(GL20.glGetUniformLocation(shader.getShader(), "colorMap"), 0);
//	       GL20.glUniform1i(GL20.glGetUniformLocation(shader.getShader(), "normalMap"), 1);
//	       
//       }
//       GL13.glActiveTexture(GL13.GL_TEXTURE1);
	}
	
//	public void setTexture(Texture t){
//		//GL13.glActiveTexture(GL13.GL_TEXTURE0);
//		//GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_REPEAT);
//		//GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL11.GL_REPEAT);
//		//t.bind();
//		this.texture=t;
//	}
//	
//	public void setNormalMap(Texture t){
////		GL13.glActiveTexture(GL13.GL_TEXTURE1);
////		GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_REPEAT);
////		GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL11.GL_REPEAT);
//		//t.bind();
//		this.bump=t;
//	}
	
	public void addTextureUnit(TextureUnit tx){
		if(tx.isNormalMap()){
			hasNormalMap=true;
		}
		this.textureUnits.add(tx);
	}
	
	
	public List<TextureUnit> getTextures(){
		return this.textureUnits;
	}
	
}